package com.cyk.websockernew.websocket

import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.stereotype.Component
import java.util.concurrent.ConcurrentHashMap
import javax.annotation.Resource
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.Session
import javax.websocket.server.PathParam
import javax.websocket.server.ServerEndpoint

data class ChatMsg (
    val targetName: String = "", //目标
    val msg: String = "",        //消息
)

/**
 * 虽然此处 @Component 默认是单例的，但是 SpringBoot 还是会为每个 WebSocket 初始化一个 bean，
 * 因此可以使用一个静态的 Set 保存起来(CopyOnWriteArraySet 相比于 HashSet 是线程安全的)
 */
@ServerEndpoint(value = "/websocket/{nickname}")
@Component
class MyWebSocket {

    companion object {
        //用来存放每个客户端对应的 MyWebSocket 对象
        private val webSocketMap = ConcurrentHashMap<String, Session>()
        private val mapper = ObjectMapper()
    }

    //与某个客户都安连接的会话，需要通过他来给客户都安发送数据
    private lateinit var session: Session //用来记录当前连接者会话
    private lateinit var nickname: String

    /**
     * 连接成功调用的方法
     */
    @OnOpen
    fun onOpen(
        session: Session,
        @PathParam("nickname") nickname: String
    ) {
        //获取当前连接客户端 session
        this.session = session
        this.nickname = nickname
        //加入到 set 中
        webSocketMap[nickname] = session
        println("$nickname 上线，当前在线人数为: ${webSocketMap.size}")
        allSend("系统消息: $nickname 上线！")
    }

    /**
     * 收到客户端消息时调用的方法
     */
    @OnMessage
    fun onMessage(messageJson: String, session: Session) {
        println("收到客户端的消息: $messageJson")
        //单独发送消息
        val message = mapper.readValue(messageJson, ChatMsg::class.java)
        val targetSession = webSocketMap[message.targetName]
        val postSession = this.session
        if(targetSession == null) {
            postSession.asyncRemote.sendText("当前用户不存在或者不在线！")
        } else {
            postSession.asyncRemote.sendText("${nickname}: ${message.msg}") //发送者获取自己的消息
            targetSession.asyncRemote.sendText("${nickname}: ${message.msg}") //接收者获取发送者的消息
        }
    }

    @OnError
    fun onError(session: Session, error: Throwable) {
        println("连接异常")
        error.printStackTrace()
    }

    @OnClose
    fun onClose() {
        webSocketMap.remove(nickname)
        println("${nickname} 下线！当前在线人数: ${webSocketMap.size}")
        allSend("系统消息: $nickname 下线！")
    }


    /**
     * 自定义群发消息
     * basicRemote: 阻塞式
     * asyncRemote: 非阻塞式
     * 大部分情况下更推荐使用 asyncRemote， 详情: https://blog.csdn.net/who_is_xiaoming/article/details/53287691
     */
    private fun allSend(message: String) {
        webSocketMap.forEach {
            it.value.asyncRemote.sendText(message)
        }
    }

}
